#pragma once
#include<Arduino.h>
#include<ESP8266WiFi.h>
#include<FloodingMesh.h>
#include <TypeConversionFunctions.h>

namespace ganix {
    namespace wifi {

        void disable_persistent() {
            // Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 .
            // This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to.
            WiFi.persistent(false);
        }

        void wifi_connect(const String &ssid, const String &passwd) {
            Serial.printf("start connect to %s\n", ssid.c_str());
            disable_persistent();
            WiFi.mode(WIFI_STA);
            WiFi.begin(ssid, passwd);
            while (WiFi.status() != WL_CONNECTED) {
                Serial.printf("connect not ready yet (status: %d), waiting...\n", WiFi.status());
                delay(500);
            }
            Serial.printf("connected, IP: (%s)\n", WiFi.localIP().toString().c_str());
        }

        void wifi_ap(IPAddress ip, IPAddress gateway, IPAddress subnet, String ap_name, String passwd) {
            disable_persistent();
             Serial.printf("starting up ap: %s\n", ap_name.c_str());
             WiFi.mode(WIFI_AP);
             WiFi.softAPConfig(ip, gateway, subnet);
             WiFi.softAP(ap_name, passwd);
             Serial.printf("started ap: %s, ip is : ", ap_name.c_str());
             Serial.println(WiFi.softAPIP());
        }

        void wifi_ap(String ap_name, String passwd) {
            wifi_ap(
                IPAddress(192,168,66,1),
                IPAddress(192,168,66,1),
                IPAddress(255,255,255,0),
                ap_name, passwd
            );
        }

        String to_mac_string(uint8_t *mac) {
            String mac_str = "";
            for (int i=0; i<6; i++) {
                char buf[5];
                sprintf(buf, "%02x", mac[i]);
                mac_str += buf;
                if (i < 5) {
                    mac_str += ':';
                }
            }
            return mac_str;
        }
        
        String get_mac() {
            uint8_t mac[6];
            WiFi.macAddress(mac);
            return to_mac_string(mac);
        }

        int get_conn() {
            return WiFi.softAPgetStationNum();
        }

        class Mesh {
        public:
            Mesh (String prefix, String suffix, String passwd, FloodingMesh::messageHandlerType cb) {
                disable_persistent();
                // A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired.
                // All ESP-NOW keys below must match in an encrypted connection pair for encrypted communication to be possible.
                // Note that it is also possible to use Strings as key seeds instead of arrays.
                uint8_t espnowEncryptedConnectionKey[16] = {0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting transmissions of encrypted connections.
                                                            0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x11
                                                           };
                uint8_t espnowHashKey[16] = {0xEF, 0x44, 0x33, 0x0C, 0x33, 0x44, 0xFE, 0x44, // This is the secret key used for HMAC during encrypted connection requests.
                                             0x33, 0x44, 0x33, 0xB0, 0x33, 0x44, 0x32, 0xAD
                                            };

                mesh = new FloodingMesh(
                    cb, 
                    passwd, 
                    espnowEncryptedConnectionKey, 
                    espnowHashKey, 
                    prefix, 
                    suffix + "-" + MeshTypeConversionFunctions::uint64ToString(ESP.getChipId()), 
                    true
                );

                mesh->begin();
                mesh->activateAP();
                Serial.printf("flooding mesh (%s%s) actived. chip_id:%d. max_len = %d\n", prefix.c_str(), suffix.c_str(), ESP.getChipId(), mesh->maxUnencryptedMessageLength());
            }

            void brocast(String message) {
                mesh->broadcast(message);
            }

            ~Mesh() {
                delete mesh;
            }
        private:
            FloodingMesh *mesh;
        };
    } // namespace wifi
}